Skip to content

Layout instance struct work - #1913

Merged
Haselnussbomber merged 4 commits into
aers:mainfrom
redstrate:work/redstrate/layout-pt1
Aug 10, 2026
Merged

Layout instance struct work#1913
Haselnussbomber merged 4 commits into
aers:mainfrom
redstrate:work/redstrate/layout-pt1

Conversation

@redstrate

Copy link
Copy Markdown
Contributor

See individual commits, I'll add more objects in a future commit as I left out a bunch of the range and path objects for now :/

I also dug into VolumetricCloud a bit more, although I have no idea where/if it's used yet (@Haselnussbomber you added it to InstanceType so maybe you know? :D)

@Haselnussbomber

Copy link
Copy Markdown
Collaborator

VolumetricCloud is not implemented yet, that's a coming Evercold feature.
@pmgr found it working on some more hexpats over at https://github.com/xivdev/file-formats/tree/main/imhex
It's the .cldb files, iirc.
I just added the name. :)

@redstrate

Copy link
Copy Markdown
Contributor Author

VolumetricCloud is not implemented yet, that's a coming Evercold feature. @pmgr found it working on some more hexpats over at https://github.com/xivdev/file-formats/tree/main/imhex It's the .cldb files, iirc. I just added the name. :)

Yeah I saw that but then couldn't figure out where the cldb files were xD but that's more of a question for them.

@pmgr

pmgr commented Aug 8, 2026

Copy link
Copy Markdown

It's not 100% in the code yet, -I think-; there's a bunch of places where the machinery is in place, but I don't think the code that produces a DrawObject out of it is in yet, or the shader (assuming it doesn't use the vfx one).

If you find that stuff out, give me a poke, 'cause it'd be fun to look into!

The only cldb I am aware of at the time I looked into it was in bgcommon/world/common/vcloud/vmodel/test.cldb

@redstrate

redstrate commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

Whoop, thanks! I can start parsing that ahead of EC, as for the code hookup I can attempt it but yeah I doubt its actually all here. The LGB code at least does read an asset path ala what it does for other drawables like BgParts. Along with some properties like color(?) as seen in the commit :)

Edit: Spent a little time hacking but to no avail: VC's CreatePrimary looks like garbage in Ghidra (which is typical Ghidra but the other CreatePrimarys do look fine) and I can't seem to find a DrawObject for it - the only unmapped one seems to be CullingBox-related. It also doesn't appear to do anything when added to a level, oh well.

@pmgr

pmgr commented Aug 8, 2026

Copy link
Copy Markdown

It also doesn't appear to do anything when added to a level, oh well.

If it's useful, their test SGBs for this specific feat. are bgcommon/world/vfx_vcloud/shared/for_vcloud/sgvc_9999_01.sgb and bgcommon/world/vfx_vcloud/shared/for_vcloud/sgvc_9999_02.sgb; for the record, they were byte-equal last I checked.

@redstrate

redstrate commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

That is useful, I didn't notice those SGBs until now either. I also did try spawning those, but they are off by default but I'm too lazy to actually try playing their animations. It's probably not worth poking until we have our hands on the benchmark, because I also couldn't find any relevant SHPKs/SHCDs :/

Oh but this is a good chance to thank you for the various file format patterns :D I always wanted to peek inside the .amb file especially, and it was nice to know I was on track with the ENV stuff

Comment on lines +3 to +39
// Client::LayoutEngine::Layer::ColliderLayer7LayoutInstance
// Client::LayoutEngine::Layer::TriggerBoxLayoutInstance
// Client::LayoutEngine::ILayoutInstance
// Client::System::Common::NonCopyable
[GenerateInterop]
[Inherits<TriggerBoxLayoutInstance>]
[StructLayout(LayoutKind.Explicit, Size = 0x90)]
public unsafe partial struct ColliderLayer7LayoutInstance;

// Client::LayoutEngine::Layer::ColliderLayer8LayoutInstance
// Client::LayoutEngine::Layer::TriggerBoxLayoutInstance
// Client::LayoutEngine::ILayoutInstance
// Client::System::Common::NonCopyable
[GenerateInterop]
[Inherits<TriggerBoxLayoutInstance>]
[StructLayout(LayoutKind.Explicit, Size = 0xA0)]
public unsafe partial struct ColliderLayer8LayoutInstance {
[FieldOffset(0x80)] private uint Unk80; // Count of something
}

// Client::LayoutEngine::Layer::ColliderLayer9LayoutInstance
// Client::LayoutEngine::Layer::TriggerBoxLayoutInstance
// Client::LayoutEngine::ILayoutInstance
// Client::System::Common::NonCopyable
[GenerateInterop]
[Inherits<TriggerBoxLayoutInstance>]
[StructLayout(LayoutKind.Explicit, Size = 0xA0)]
public unsafe partial struct ColliderLayer9LayoutInstance;

// Client::LayoutEngine::Layer::ColliderLayer10LayoutInstance
// Client::LayoutEngine::Layer::TriggerBoxLayoutInstance
// Client::LayoutEngine::ILayoutInstance
// Client::System::Common::NonCopyable
[GenerateInterop]
[Inherits<TriggerBoxLayoutInstance>]
[StructLayout(LayoutKind.Explicit, Size = 0x1270)]
public unsafe partial struct ColliderLayer10LayoutInstance;

@Haselnussbomber Haselnussbomber Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually know what these are? If not, I don't really like to see them being added.

@Haselnussbomber Haselnussbomber Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, have a look at https://github.com/NotAdam/Lumina/blob/master/src/Lumina/Data/Parsing/Layer/LayerCommon.cs
This is quite old, but it could still match with what is copied in the SetProperties functions. :)
(Not for these ColliderLayers, just as a general note)

@pmgr pmgr Aug 9, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually know what these are? If not, I don't really like to see them being added.

Try these on for size:

0x56  ColliderLayer7   -> WaterRange            -> WaterRangeLayoutInstance
0x57  ColliderLayer8   -> ShowHideRange         -> ShowHideRangeLayoutInstance
0x58  ColliderLayer9   -> GameContentsRange     -> GameContentsRangeLayoutInstance
0x59  ColliderLayer10  -> EventEffectRange      -> EventEffectRangeLayoutInstance

@redstrate redstrate Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go ahead and use those names - those are taken from the layer groups they're in (I assume) so I'm happy with that. The only one I'm not suuper sure about and can't confirm is ColliderLayer9/GameContentsRange, do you know where that's used?

@pmgr pmgr Aug 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not aware of 0x58/GameContentsRange being used anywhere, to be honest. And I suspect it's not actually "hooked up" to much of anything (yet? anymore?).

If anyone wants to bother doing a little digging/confirming:
WaterRangeLayoutInstance had 1 byte, first bit is 'Swimming' state, second is 'Underwater' state.

ShowHideRangeLayoutInstance had something like:

/* 0x80 */ uint count;
/* 0x88 */ LayerSetRef *refs;
/* 0x90 */ bool active:

Where LayerSetRef is just an array of uints (each one of those being a layerset id), and count is how many 'refs' there are.

I -believe- (!) that GameContentsRangeLayoutInstance has the same fields/semantics. So does EventEffectRangeLayoutInstance.

But EventEffectRangeLayoutInstance then also has something like:

/* 0x0094 */ float effectPointSpacing;
/* 0x0098 */ float effectFarClipBegin;
/* 0x009C */ float effectFarClipEnd;
/* 0x00A0 */ Vector3 effectBoundsCenter;
/* 0x00AC */ float effectBoundsRadius;
/* 0x00B0 */ bool effectsEnabled;
/* 0x00B4 */ uint effectPointCount;
/* 0x00B8 */ Vector3* effectPositions; // number of elements is effectPointCount, pretty sure this uses the 16-byte Vector3 variant
/* 0x00C0 */ Quaternion *effectRotations; // I don't remember what ffxivcs/SE calls it
/* 0x00C8 */ uint *effectIndices; // this is a mutable permutation of indices into effectPositions/effectRotations
/* 0x00D0 */ ActiveEffect activeEffects[185];
/* 0x1228 */ uint activeEffectsCount;
/* 0x1230 */ SpatialNode root;

And then the structs are likely close to:

struct ActiveEffect { // size 0x18
  /* 0x00 */ VfxObject *vfxObject; // think we already have it in Scene somewhere
  /* 0x08 */ SpatialNode *leaf;
  /* 0x10 */ uint effectIndex;
};

struct SpatialNode { // size 0x40
  /* 0x00 */ SpatialNode *childA;
  /* 0x08 */ SpatialNode *childB;
  /* 0x10 */ Vector3 boundsMin; // also the 16-byte variant used on these two
  /* 0x20 */ Vector3 boundsMax;
  /* 0x30 */ int effectIndexStart; // this is an offset into the effectIndices array in EventEffectRangeLayoutInstance
  /* 0x34 */ int availableEffectCount;
};

Essentially it's building a spatial search tree with a whole bunch of effect points, each one has AABB (boundsmin/max).

The effectIndexStart is what index in the effectIndices array this specific node's effects start at, availableEffectCount is how many elements are currently available.

So, if I understood this right (bold fucking claim), it works a little like this:

With: effectIndices = [3, 1, 0, 2, 6, 4, 5]

Player gets close enough to a leaf SpatialNode to spawn an Effect, say it has effectIndexStart of 2, and availableEffectCount of 2, so that'd cover the [0, 2] slice of the effectIndices array.
It selects 0 (first available effect), copies the next element over (2) to its spot, and decrements availableEffectCount.

So you get effectIndices = [3, 1, 2, 2, 6, 4, 5]

I realize this is a lot, so it's here just as a "if anyone wants to give a shit testing this out/confirming it experimentally, go for it"

@redstrate redstrate Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that is extremely useful but I'll probably wait until my next layout PR (or after) :) I want to keep these small and reviewable. You're of course free to add on top though!

I'm not aware of 0x58/GameContentsRange being used anywhere, to be honest. And I suspect it's not actually "hooked up" to much of anything (yet? anymore?).

It is actually strange yeah, I would expect it to be used somewhere considering its location in the enum. But I wouldn't be surprised if we can't find it because its one of the stripped out ones ala BNpcs, Gatherings, QuestMarkers etc.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's completely fair; just ping me or something if you ever end up PRing it, would be cool to see what's wrong/right

This is what it's used for I guess.
These are read in another init function (not SetProperties) and are all
booleans, although I have no idea what they do yet.
These are all read by the client - although some, especially the
server-side/development ones - don't do much but still show up in the
disassembly and have vtables.

There's a lot more instance objects to go, but I got tired...
@redstrate
redstrate force-pushed the work/redstrate/layout-pt1 branch from 75d32b9 to a69b0e9 Compare August 10, 2026 00:49
Thanks to @pmgr for the idea on how to name these.
@Haselnussbomber
Haselnussbomber merged commit 076ba9d into aers:main Aug 10, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants